var testBattery = [];

function registerTest(testFunction)
{
	testBattery.push(testFunction);
}

function registerTests(testFunctionArray)
{
	for(var i = 0; i < testFunctionArray.length; i++)
		if(testFunctionArray[i])
			testBattery.push(testFunctionArray[i]);
}

function assert()
{
	this.areEqual = function(a, b, failureMessage)
	{
		if(a != b) throw failureMessage || ("Expected " + b + " but got " + a);
	};
	this.areNotEqual = function(a, b, failureMessage)
	{
		if(a == b) throw failureMessage || ("Expected anything but " + b + " but got " + b);
	};
	this.isDefined = function(a, failureMessage)
	{
		if(a == null || typeof(a) == "undefined") throw failureMessage || ("Expected a defined object but got " + a);
	};
	this.isNotDefined = function(a, failureMessage)
	{
		if(a != null && typeof(a) != "undefined") throw failureMessage || ("Expected undefined but got " + a);
	};
	this.isTrue = function(a, failureMessage)
	{
		if(!a) throw failureMessage || "Expected TRUE but got FALSE";
	};
	this.isFalse = function(a, failureMessage)
	{
		if(a) throw failureMessage || "Expected FALSE but got TRUE";
	};
	this.throwsException = function(func, failureMessage)
	{
		try { func(); throw failureMessage || "Expected an exception to be thrown, it wasn't";}
		catch (error) { }
	};
	this.doesntThrowException = function(func, failureMessage)
	{
		try { func(); }
		catch (error) { throw failureMessage || "Expected no exceptions but one occurred"; }
	};
}

var Assert = new assert();
var currentTest = 0;
var nPassed = 0;
var nFailed = 0;
var realTestCount;
var onResult;
var onComplete;
var onStatusUpdate;

function runNextTest() {
	var exp = new RegExp(/\s.*\(/gi);
	var cmthd = testBattery[currentTest];
	if(cmthd) {
		if(window["console"])
			console.log(cmthd);
		var methodName = exp.exec(cmthd.toString());
		methodName = (methodName || " ").toString();
		methodName = methodName.substring(0, methodName.length - 1).trim();
		if(onStatusUpdate)
			onStatusUpdate(methodName, currentTest + 1, realTestCount, nPassed, nFailed);
		if(window["console"])
			console.log("Now running test " + methodName + " (#" + (currentTest + 1) + " of " + realTestCount + ")");
		try{
			var startTime = new Date();
			cmthd();
			var endTime = new Date();
			var rawTime = endTime - startTime;
			var elapsedTime = (rawTime / 1000);
			var elapsedTimeMsg = "Completed in " + (elapsedTime >= 1 ? elapsedTime + " seconds" : (rawTime >= 1 ? (rawTime + " ms") : "< 1 ms"));
			if(onResult)
				onResult(methodName, null, elapsedTimeMsg, cmthd);
			if(window["console"])
				console.log(methodName + " (#" + (currentTest + 1) + " of " + realTestCount + " passed)");
			nPassed++;
		}
		catch(error){
			nFailed++;
			if(onResult)
				onResult(methodName, error);
			if(window["console"])
				console.log("Test " + (i + 1) + " failed" + (error ? ": " + error : ""));
		}
	}
	currentTest++;
	if(currentTest < realTestCount)
	{
		setTimeout(runNextTest, 1);
	}
	else if(onComplete)
		onComplete(nPassed, nFailed, realTestCount);
}

function runAllTests(_onResult, _onComplete, _onStatusUpdate)
{
	if(window["console"])
		console.log("Test run starting...");
	nPassed = 0;
	nFailed = 0;
	realTestCount = testBattery.length;
	setTimeout(runNextTest, 5);
	onResult = _onResult;
	onComplete = _onComplete;
	onStatusUpdate = _onStatusUpdate;
}
